/*
 *  Copyright (C) 2011 JiangHongTiao <jjurco.sk_gmail.com>
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package sk.lieskove301.jianghongtiao.liaad.prisoner.memory;

/**
 * Date of create: May 15, 2011
 *
 * @author JiangHongTiao <jjurco.sk_gmail.com>
 * @version 2011.0515
 */
public class Memory {

    private double OFI;

    private boolean memoryFor;
    private int lastRepetition = 0;
    private int learningTime = 0;

    private double SN = 0;
    private double RET = 0;
    private double STAB = 0;
    private double BASE_FACTOR = 0;
    private double finalResut = -1;

    public Memory(boolean memoryFor, double ofi, double initStab, double baseFactor) {
        this.memoryFor = memoryFor;
        this.SN = compSN(ofi);
        this.OFI = ofi;
        this.STAB = initStab;
        this.BASE_FACTOR = baseFactor;
    }

    public void putMemento(boolean memento){
        finalResut = -1;
        if(memento == memoryFor){
            //count
            if(learningTime != 0){
                //compute base factor and stability before
                BASE_FACTOR = compBaseFactor();
                STAB = compSTAB();
                RET = compRET(lastRepetition);
            } else {
                RET = compRET(0.0);
            }
            lastRepetition = 0;
            learningTime++;
        } else {
            if(learningTime > 0){
                lastRepetition++;
            }
        }
    }

    public double getMemory(){
        if(finalResut == -1){
//            System.out.println(lastRepetition);
            finalResut = compRET(lastRepetition);
        }
        return finalResut;
    }

    public boolean isRemembered(){
        if(finalResut == -1){
            finalResut = compRET(lastRepetition);
        }
        return finalResut > 1-OFI;
    }

    private double compSN(double ofi){
        return Math.log(1/(1-ofi))/Math.log(2);
    }

    private double compRET(double m){
        double result = 1/(Math.pow(2, (m*SN/STAB)));
        if((result < 0) || (result > 1)){
            throw new IllegalStateException("RET is bad implemented. It should be between 0..1 and was:" + RET);
        } else return  result;
    }

    private double compSTAB(){
        return STAB * compCHNG(RET, OFI, BASE_FACTOR);
    }

    private double compBaseFactor(){
        return compCHNG(1.0-OFI, OFI, BASE_FACTOR);
    }

    /**
     * 
     * @param ret last RET value
     * @param ofi OFI initial value
     * @param baseFactor computed base factor
     * @return computed CHNG value
     */
    private double compCHNG(double ret, double ofi, double baseFactor){
        if(ret >= 1-ofi){
            double a1 = (1-baseFactor) / Math.pow(ofi, 2);
            double b1 = -2.0 * (1-ofi) * a1;
            double c1 = 1-(1-2.0 * (1-ofi))*a1;
            return a1 * Math.pow(ret, 2) + b1*ret + c1;
        } else {
            double a2 = (-1.0 * baseFactor) / Math.pow(1-ofi, 2);
            double b2 = -2.0 * (1-ofi) * a2;
            double c2 = 0;
            return a2 * Math.pow(ret, 2) + b2*ret + c2;
        }
    }

}
